package org.yagi;

import org.antlr.runtime.*;
import alice.tuprolog.*;

import java.io.*;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.Scanner;
//import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

public class YAGICommandLine {

	private final static Logger LOGGER = Logger.getLogger(YAGICommandLine.class.getName());
	
	public static void main(String[] args) throws Exception, IOException,
			RecognitionException {

		// Set logging level
		//LOGGER.setLevel(Level.OFF); 
		//LOGGER.setLevel(Level.INFO); 
		LOGGER.setLevel(Level.FINER);
		
		//ConsoleHandler handler = new ConsoleHandler();
        //PUBLISH this level
        //handler.setLevel(Level.FINER);
        //LOGGER.addHandler(handler);
        
		// Initializing HashMaps for fluents and actions
		Map<String, Integer> fluents = new HashMap<String,Integer>();
		Map<String, Integer> actions = new HashMap<String,Integer>();
		
		// Initializing Prolog engine
	    LOGGER.log(Level.FINE, "Initializing Prolog engine");
		Prolog engine = new Prolog();
		
		// Locating Golog interpreter Prolog file
	    LOGGER.log(Level.FINER, "Locating Golog interpreter Prolog file");
		//ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	    InputStream input  = YAGICommandLine.class.getClassLoader().getResourceAsStream("gologinterpreter.pl");

		// Loading Golog interpreter
		engine.setTheory(new Theory(input));
		input.close();
	    LOGGER.log(Level.FINE, "Internal Golog interpreter initialized");
		
		// Locating Simple Elevator domain Prolog	    
	    LOGGER.log(Level.FINER, "Locating Simple Elevator Prolog file");
		input  = YAGICommandLine.class.getClassLoader().getResourceAsStream("simpleElevator.pl");
			    
		// Loading Simple Elevator 
		engine.addTheory(new Theory(input));
		input.close();
	    LOGGER.log(Level.FINE, "Simple Elevator example initialized");
	    
	    // Initialize fluents table
	    fluents.put("ffloors", new Integer(1));
	    fluents.put("fon", new Integer(1));
	    fluents.put("fcurrFloor", new Integer(1));
	    
	    // Display YAGI info
	    System.out.println("YAGI 0.2");
	    System.out.println("Minimal implementation with the Elevator example imported.");
	    System.out.println("Note: progression and declaring new actions not supported (among other things).");
	    System.out.println("Type \"help\" to see examples of well-formed YAGI statements. ");
	    
	    // YAGI Interpreter loop
		Boolean multipleLines = false;
		String multipleLinesInput = "";
	    Scanner scanner = new Scanner(System.in);
		while (true) {
			if (!multipleLines){
				System.out.print("YAGI>> ");
			}
			String line = scanner.nextLine().trim();
			if ("quit".equals(line) || "exit".equals(line)){
				break;
			}
			if ("help".equals(line)){
			    System.out.println("");
			    System.out.println("Examples:");
			    System.out.println("");
			    System.out.println("YAGI>> choose up(5); or down(5);");
			    System.out.println("");
			    System.out.println("YAGI>> pick $n from on such goto($n); end pick");
			    System.out.println("");
			    System.out.println("YAGI>> pick $n from on such \\");
			    System.out.println("         choose up($n); \\");
			    System.out.println("         or down($n); \\");
			    System.out.println("         end choose \\");
			    System.out.println("       end pick");
			    System.out.println("");
			    System.out.println("YAGI>> serveafloor();");
			    System.out.println("");
			    System.out.println("YAGI>> serveafloor(); serveafloor();");
			    System.out.println("");
			    System.out.println("YAGI>> on");
			    System.out.println("");
			    System.out.println("YAGI>> on+={1,7,8};");
			    System.out.println("");
			    System.out.println("YAGI>> on-={3,8};");
			    System.out.println("");
				continue;
			}
			if (line.length()==0)
				continue;
			
			if (line.charAt(line.length()-1) == '\\'){
				multipleLines = true;
				line = line.substring(0, line.length()-1);
				multipleLinesInput += line;
				continue;
			} else {
				multipleLines = false;
				line = multipleLinesInput + line;
				multipleLinesInput = "";				
			}
			LOGGER.log(Level.FINER, "Input scanned to be passed to the YAGI interpreter: " + line);
			CharStream stream = new ANTLRStringStream(line);
			YAGILexer lexer = new YAGILexer(stream);
			TokenStream tokenStream = new CommonTokenStream(lexer);
			YAGIParser parser = new YAGIParser(fluents, actions, engine, tokenStream, LOGGER);
		    YAGIParser.line_return l = parser.line();
		    String output = l.ret;
		    if ((output != null) && (output.charAt(0)=='[')){	
		    	output = output.substring(1,l.ret.length()-1);
		    	List<String> signals = Arrays.asList(output.split("\\s*,\\s*"));
		    	for (int i=0; i<signals.size(); i++)
		    		System.out.println(signals.get(i));
		    }
		    else{
				System.out.println(output);
		    			    }
		}
		scanner.close();
	}

	public static void testProlog() throws Exception {
		System.out.println("Testing Prolog");
		Prolog engine = new Prolog();
		
		// Get the Prolog file for the Golog interpreter  
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		URL url = classLoader.getResource("Prolog/gologinterpreter.pl");
		File file = new File(url.getPath());
		FileInputStream input = new FileInputStream(file);

		// Build a new Prolog engine and initialize it with this theory
		engine.setTheory(new Theory(input));
		input.close();

		// Get the file for the Simple Elevator domain 
		url = classLoader.getResource("Prolog/simpleElevator.pl");
		file = new File(url.getPath());
		input = new FileInputStream(file);
		
		// Add this theory to the Prolog engine
		engine.addTheory(new Theory(input));
		input.close();

		// Try some queries
		SolveInfo info;

		// A very simple query that does not rely on Golog 
		info = engine.solve("assert(aaaaaa(1)).");
		info = engine.solve("aaaaaa(X).");
		System.out.println(info.getSolution());
		System.out.println(info.getTerm("X"));

		// A test query about s0
		info = engine.solve("do(?(on(5)), s0,S).");
		System.out.println(info.getTerm("S"));

		// A Golog query about s0
		info = engine.solve("do(serve(3), s0, S).");
		System.out.println(info.getTerm("S"));
	}
	
}